package coder.nav.service;

import coder.nav.enums.NavIdeLangEnums;
import coder.nav.enums.NavSettingEnums;
import coder.nav.mapper.NavSettingMapper;
import coder.nav.model.NavMemo;
import coder.nav.model.NavSetting;
import coder.nav.model.NavUser;
import coder.nav.model.bo.NavSaveSettingBO;
import coder.nav.model.vo.NavListVO;
import coder.nav.model.vo.setting.*;
import coder.nav.utils.JwtUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class NavSettingService extends ServiceImpl<NavSettingMapper, NavSetting> {

    @Autowired
    private NavUserService userService;

    @Autowired
    private NavListService listService;

    @Autowired
    private NavMemoService memoService;

    /**
     * 获取用户配置列表
     */
    public List<NavSettingVO> getSettingList() {
        return this.getSettingList(Boolean.TRUE);
    }

    /**
     * 获取用户配置列表
     * @param isSwagger 是否显示字段说明
     */
    public List<NavSettingVO> getSettingList(Boolean isSwagger) {
        // 获取配置列表
        LambdaQueryWrapper<NavSetting> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavSetting::getUserId, JwtUtils.getUserId());
        List<NavSetting> list = this.list(queryWrapper);
        // 账号/密码/邮箱
        NavUser user = userService.getByToken();
        list.add(new NavSetting(NavSettingEnums.USERNAME.getKey(), user.getUsername()));
        list.add(new NavSetting(NavSettingEnums.PASSWORD.getKey(), ""));
        // 转 map
        Map<String, String> map = list.stream().collect(Collectors.toMap(NavSetting::getType, NavSetting::getParams, (k1, k2) -> k1));
        List<NavSettingVO> result = new ArrayList<>();

        NavSettingEnums[] es = NavSettingEnums.values();
        for(NavSettingEnums e : es) {
            String params = null;
            switch(e) {
                case NAV:
                case HINT_JAVA:
                case HINT_PHP:
                case HINT_JAVASCRIPT:
                    params = map.get(e.getKey());
                    if(StringUtils.isBlank(params) && e == NavSettingEnums.NAV) {
                        List<NavListVO> navList = listService.getList();
                        params = JSON.toJSONString(navList, SerializerFeature.PrettyFormat);
                    }
                    params = this.dealSettingArray(e.getClazz(), params, isSwagger);
                    break;
                default:
                    params = this.dealSettingObject(e.getClazz(), map.get(e.getKey()), isSwagger);
            }
            String desc = e.getIcon() + " " + e.getValue();
            if(e == NavSettingEnums.USERNAME) {
                desc += "（ID: " + user.getId() + "）";
            }
            result.add(new NavSettingVO(e.getKey(), desc, params));
        }
        return result;
    }

    /**
     * 处理用户配置数据（数组）
     * @param clazz 配置类型
     * @param params 配置内容（json）
     * @param isSwagger 是否显示字段说明
     */
    public String dealSettingArray(Class<?> clazz, String params, Boolean isSwagger) {
        List<?> list = JSON.parseArray(params, clazz);
        List<String> result = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(list)) {
            for(Object item : list) {
                result.add(this.dealSettingObject(clazz, JSON.toJSONString(item), false));
            }
        }
        StringBuilder message = new StringBuilder();
        if(isSwagger) {
            message.append("/** \n");
            Field[] fields = clazz.getDeclaredFields();
            for(Field column : fields) {
                ApiModelProperty[] apis = column.getAnnotationsByType(ApiModelProperty.class);
                if(apis.length > 0) {
                    String columnDesc = apis[0].value();
                    message.append(" * @param  ").append(column.getName()).append("\t").append(columnDesc).append("\n");
                }
            }
            message.append(" */ \n");
        }
        return message + JSON.toJSONString(JSON.parseArray(result.toString()), SerializerFeature.PrettyFormat);
    }

    /**
     * 处理用户配置数据（对象）
     * @param clazz 配置类型
     * @param params 配置内容（json）
     * @param isSwagger 是否显示字段说明
     */
    public String dealSettingObject(Class<?> clazz, String params, Boolean isSwagger) {
        String result = null;
        try {
            if(clazz == String.class) {
                result = params;
                if(StringUtils.isBlank(result) || "null".equals(result)) {
                    result = "";
                }
            } else {
                if(StringUtils.isEmpty(params)) {
                    result = JSON.toJSONString(clazz.newInstance(), SerializerFeature.PrettyFormat);
                } else {
                    // 将json字符串转成Java对象后再转回json字符串，确保最终的json字符串字段按照ordinal排序
                    result = JSON.toJSONString(JSON.toJavaObject(JSONObject.parseObject(params), clazz), SerializerFeature.PrettyFormat);
                }
            }
            if("null".equals(result)) {
                result = JSON.toJSONString(clazz.newInstance(), SerializerFeature.PrettyFormat);
            }
            // 带上 swagger 字段备注
            if(isSwagger && clazz != String.class) {
                String[] strArr = result.split("\\n");
                StringBuilder sb = new StringBuilder();
                for(int j = 0; j < strArr.length; j++) {
                    String temp = strArr[j];
                    if(j > 0 && j < strArr.length - 1) {
                        String columnName = strArr[j].split(":")[0].trim().replaceAll("\"", "");
                        Field column = clazz.getDeclaredField(columnName);
                        ApiModelProperty[] apis = column.getAnnotationsByType(ApiModelProperty.class);
                        if(apis.length > 0) {
                            String columnDesc = apis[0].value();
                            if(StringUtils.isNotBlank(columnDesc)) {
                                temp = "\t// " + columnDesc + "\n" + temp;
                                if(j < strArr.length - 2) {
                                    temp += "\n";
                                }
                            }
                        }
                    }
                    temp = temp.replaceAll("\t", "    ");
                    sb.append(temp).append("\n");
                }
                result = sb.toString();
            }
        } catch(Exception ex) {
            return null;
        }
        return result;
    }

    /**
     * 保存用户配置
     * @param bo 入参
     */
    public void saveSetting(NavSaveSettingBO bo) {
        NavSettingEnums key = NavSettingEnums.getByKey(bo.getKey());
        String params = bo.getParams();
        switch(key) {
            case USERNAME: userService.setUsernameByToken(params); break;
            case PASSWORD: userService.setPasswordByToken(params); break;
            case NOTICE: this.saveSetting(NavSettingEnums.NOTICE.getKey(), params, false); break;
            case CASH: this.saveSetting(NavSettingEnums.CASH.getKey(), params, false); break;
            case MEMO: this.saveSetting(NavSettingEnums.MEMO.getKey(), params, false); break;
            case IDE: this.saveSetting(NavSettingEnums.IDE.getKey(), params, false); break;
            case NAV: this.saveSetting(NavSettingEnums.NAV.getKey(), params, true); break;
            case HINT_JAVA: this.saveSetting(NavSettingEnums.HINT_JAVA.getKey(), params, true); break;
            case HINT_PHP: this.saveSetting(NavSettingEnums.HINT_PHP.getKey(), params, true); break;
            case HINT_JAVASCRIPT: this.saveSetting(NavSettingEnums.HINT_JAVASCRIPT.getKey(), params, true); break;
        }
    }

    /**
     * 保存用户配置
     * @param key 配置类型
     * @param params 配置内容（json）
     * @param isArray 是否数组型配置
     */
    public void saveSetting(String key, String params, Boolean isArray) {
        LambdaQueryWrapper<NavSetting> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavSetting::getUserId, JwtUtils.getUserId())
                .eq(NavSetting::getType, key);
        this.remove(queryWrapper);
        NavSetting setting = new NavSetting();
        setting.setUserId(JwtUtils.getUserId());
        setting.setType(key);
        if(isArray) {
            setting.setParams(JSON.toJSONString(JSON.parseArray(params), SerializerFeature.PrettyFormat));
        } else {
            setting.setParams(JSON.toJSONString(JSON.parseObject(params), SerializerFeature.PrettyFormat));
        }
        this.save(setting);
    }

    /**
     * 获取配置
     * @param enums 配置类型
     * @param userId 用户id
     */
    public String getSetting(NavSettingEnums enums, Long userId) {
        LambdaQueryWrapper<NavSetting> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavSetting::getType, enums.getKey())
                .eq(NavSetting::getUserId, userId);
        NavSetting setting = this.getOne(queryWrapper);
        String params = null;
        if(Objects.nonNull(setting)) {
            params = setting.getParams();
        }
        switch(enums) {
            case NAV:
            case HINT_JAVA:
            case HINT_PHP:
            case HINT_JAVASCRIPT:
                return this.dealSettingArray(enums.getClazz(), params, false);
            default:
                return this.dealSettingObject(enums.getClazz(), params, false);
        }
    }

    /**
     * 获取消息通知配置
     * @param userId 用户id
     */
    public NavNoticeSettingVO getNoticeSetting(Long userId) {
        String params = this.getSetting(NavSettingEnums.NOTICE, userId);
        return JSON.toJavaObject(JSON.parseObject(params), NavNoticeSettingVO.class);
    }

    /**
     * 获取记账本配置
     */
    public NavCashSettingVO getCashSetting() {
        return this.getCashSetting(JwtUtils.getUserId());
    }

    /**
     * 获取记账本配置
     * @param userId 用户id
     */
    public NavCashSettingVO getCashSetting(Long userId) {
        String params = this.getSetting(NavSettingEnums.CASH, userId);
        return JSON.toJavaObject(JSON.parseObject(params), NavCashSettingVO.class);
    }

    /**
     * 获取用户所有配置
     */
    public Map<Long, NavCashSettingVO> getAllUserCashSettingMap() {
        LambdaQueryWrapper<NavSetting> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavSetting::getType, NavSettingEnums.CASH.getKey());
        List<NavSetting> list = this.list(queryWrapper);
        if(CollectionUtils.isEmpty(list)) {
            return new HashMap<>();
        }
        Map<Long, NavCashSettingVO> map = new HashMap<>();
        for(NavSetting item : list) {
            String params = this.dealSettingObject(NavSettingEnums.CASH.getClazz(), item.getParams(), Boolean.FALSE);
            map.put(item.getUserId(), JSON.toJavaObject(JSON.parseObject(params), NavCashSettingVO.class));
        }
        return map;
    }

    /**
     * 获取编辑器配置
     */
    public NavIdeSettingVO getIdeSetting() {
        return this.getIdeSetting(JwtUtils.getUserId());
    }

    /**
     * 获取编辑器配置
     * @param userId 用户id
     */
    public NavIdeSettingVO getIdeSetting(Long userId) {
        String params = this.getSetting(NavSettingEnums.IDE, userId);
        return JSON.toJavaObject(JSON.parseObject(params), NavIdeSettingVO.class);
    }

    /**
     * 获取记事本配置
     */
    public NavMemoSettingVO getMemoSetting() {
        return this.getMemoSetting(JwtUtils.getUserId());
    }

    /**
     * 获取记事本配置
     * @param userId 用户id
     */
    public NavMemoSettingVO getMemoSetting(Long userId) {
        String params = this.getSetting(NavSettingEnums.MEMO, userId);
        return JSON.toJavaObject(JSON.parseObject(params), NavMemoSettingVO.class);
    }

    /**
     * 获取语言补全配置
     * @param lang 语言
     */
    public List<NavCodeMirrorHintVO> getHintSetting(String lang) {
        return this.getHintSetting(lang, JwtUtils.getUserId());
    }

    /**
     * 获取语言补全配置
     * @param lang 语言
     * @param userId 用户id
     */
    public List<NavCodeMirrorHintVO> getHintSetting(String lang, Long userId) {
        NavIdeLangEnums langEnum = NavIdeLangEnums.getByCode(lang);
        String params = "";
        switch(langEnum) {
            case JAVA:
                params = this.getSetting(NavSettingEnums.HINT_JAVA, userId);
                break;
            case PHP:
                params = this.getSetting(NavSettingEnums.HINT_PHP, userId);
                break;
            case JAVASCRIPT:
                params = this.getSetting(NavSettingEnums.HINT_JAVASCRIPT, userId);
                break;
        }
        List<NavCodeMirrorHintVO> list = JSONArray.parseObject(params, List.class);
        if(CollectionUtils.isEmpty(list)) {
            list = new ArrayList<>();
        }
        // coder.nav
        List<NavMemo> utils = memoService.getCoderNavPackage(lang, userId);
        List<NavCodeMirrorHintVO> functions = new ArrayList<>();
        for(NavMemo memo : utils) {
            // 代码
            String code = memo.getContext().replaceAll("```.*", "").trim();
            // 类名称
            Pattern classPattern = Pattern.compile("class \\w*");
            Matcher classMatcher = classPattern.matcher(code);
            String clazz = "";
            if(classMatcher.find()) {
                clazz = classMatcher.group();
                clazz = clazz.replaceAll("class ", "").trim();
            }
            // 方法名
            String regex = "\\b(?!"
                    + "if|else|for|while|do|switch|case|try|catch|finally|return|"
                    + "class|interface|extends|implements|static|public|private|protected|"
                    + "function|var|let|const|new|this|super|void|boolean|char|byte|short|"
                    + "int|long|float|double|true|false|null"
                    + "\\b)"
                    + "([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*"
                    + "\\([^)]*\\)";
            Pattern functionPattern = Pattern.compile(regex);
            if(langEnum == NavIdeLangEnums.JAVA || langEnum == NavIdeLangEnums.PHP) {
                functionPattern = Pattern.compile("public.*" + regex);
            }
            Matcher functionMatcher = functionPattern.matcher(code);
            while(functionMatcher.find()) {
                String function = functionMatcher.group();
                // 去掉 public 再匹配一次
                Pattern itemPattern = Pattern.compile(regex);
                Matcher itemMatcher = itemPattern.matcher(function);
                while(itemMatcher.find()) {
                    function = itemMatcher.group();
                    switch(langEnum) {
                        case JAVA:
                            function = clazz + "." + function;
                            break;
                        case PHP:
                            function = clazz + "::" + function;
                            break;
                    }
                    NavCodeMirrorHintVO hintVO = new NavCodeMirrorHintVO();
                    hintVO.setDisplayText("🧩 " + function);
                    hintVO.setTrigger(function);
                    hintVO.setText(function.replaceAll("\\(.*\\)", "") + "()");
                    functions.add(hintVO);
                }
            }
        }
        list.addAll(functions);
        return list;
    }

}
